// -- Includes --

// C++
#include <cstdlib>
#include <iostream>

// Mine
#include "opcodes.h"
#include "cpu.h"
#include "main.h"

// So we can access Kestrel stuff
using namespace Kestrel;

// -- Name Space -- 
namespace Opcodes
{
        
    // -- Structs --
    
    // -- Variables --
    
    // -- Functions --
    
        // Load / Store
            // LDA - Set Accumulator
            void LDA(unsigned char Mem)
            {
             
                // Set A to Mem
                CPU->A = Mem;
                
                // Set Flags
                SetFlags(Mem);
                
            }
            // LDX - Set X
            void LDX(unsigned char Mem)
            {
             
                // Set X to Mem
                CPU->X = Mem;
                
                // Set Flags
                SetFlags(Mem);
                
            }
            // LDY - Set Y
            void LDY(unsigned char Mem)
            {
             
                // Set Y to Mem
                CPU->Y = Mem;
                
                // Set Flags
                SetFlags(Mem);
                
            }
            // STA - Store Accumulator
            void STA(unsigned char* Mem)
            {
             
                // Set Mem to A
                *Mem = CPU->A;
                
            }
            // STX - Store X
            void STX(unsigned char* Mem)
            {
             
                // Set Mem to X
                *Mem = CPU->X;
                
            }
            // STY - Store Y
            void STY(unsigned char* Mem)
            {
             
                // Set Mem to Y
                *Mem = CPU->Y;
                
            }
            
        // Register Transfers
            // Pseudo Opcode, as we can use one function for all the opcode, TAX, TAY, TXA and TYA.
            void Transfer(unsigned char From, unsigned char* To, bool bSetFlags)
            {
             
                // Tranfer
                *To = From;
                
                // Flags - Only if we have to
                if (bSetFlags)
                    SetFlags(*To);
                
            }
            
        //  Stack Operations
            // TSX/TXS - Can use the Psuedo-Register tranfer function.   
            // PHA - Push Accumulator onto stack
            void PHA()
            {
             
                // Do it
                RAM->PushToStack(CPU->A);
                
            }
            // PHP - Push Processor Status onto stack
            void PHP()
            {
                
                // Do it
                RAM->PushToStack(CPU->FlagsToByte());
                
            }
            // PLA - Pull Accumulator from stack
            void PLA()
            {
             
                // Pull...
                CPU->A = RAM->PullFromStack();
                
            }
            // PLP - Pull Processor Status from stack
            void PLP()
            {
                
                // Pull!
                CPU->FlagsFromByte(RAM->PullFromStack());
                
            }

        // Logical
            // AND - Logical AND
            void AND(unsigned char Mem)
            {
             
                // And A and Mem
                CPU->A = CPU->A & Mem;
                
                // Set Flags
                SetFlags(CPU->A);
                
            }
            // EOR - Logical Exclusive OR
            void EOR(unsigned char Mem)
            {
             
                // XOR A and Mem
                CPU->A = CPU->A ^ Mem;
                
                // Set Flags
                SetFlags(CPU->A);
                
            }
            // ORA - Logical OR
            void ORA(unsigned char Mem)
            {
             
                // OR A and Mem
                CPU->A = CPU->A | Mem;
                
                // Set Flags
                SetFlags(CPU->A);
                
            }
            // BIT - Bit test
            void BIT(unsigned char Mem)
            {
                
                // AND mem with A
                unsigned char Result = CPU->A & Mem;
                
                // Set Flags
                    // Zero Flag - If zero
                    if (Result == 0)
                        CPU->ZeroFlag = true;
                    else
                        CPU->ZeroFlag = false;
                    // Overflow Flag - Set to bit 6 of the result
                    CPU->OverflowFlag = (bool) ((Result & 64) >> 6);
                    // Negative Flag - Set to bit 7 of the result
                    CPU->NegativeFlag = (bool) ((Result & 128) >> 7);
                
            }
            
        // Arithmetic
            // ADC - Add with carry
            void ADC(unsigned char Mem)
            {
                
                // Add CarryBit to Mem
                Mem += (int) CPU->CarryFlag;

                // Also remember the old Accumulator value to check if we carried
                unsigned char Old_A = CPU->A;
                
                // Perform addition
                CPU->A += Mem;
                
                // Set Flags
                    // Negative / Zero flag
                    SetFlags(CPU->A);
                    // Carry Flag - If we've wrapped to 0
                    CPU->CarryFlag = (CPU->A < Old_A) ? true : false;
                    // Overflow Flag - Do the sum, but as unsigned, and compare the value of it
                    // with the [unsigned version of the] value we got earlier :)
                    char SignedSum = ((char) Old_A) + (( char) Mem);
                    if (((char) CPU->A) != SignedSum)
                        CPU->OverflowFlag = true;
                    else
                        CPU->OverflowFlag = false;
                
            }
            // SBC - Subtract with carry
            void SBC(unsigned char Mem)
            {
                
                // Add CarryBit to Mem
                Mem += (int) CPU->CarryFlag;

                // Also remember the old Accumulator value to check if we carried
                unsigned char Old_A = CPU->A;
                
                // Perform subtraction
                CPU->A -= Mem;
                
                // Set Flags
                    // Negative / Zero flag
                    SetFlags(CPU->A);
                    // Carry Flag - If we've wrapped to 0
                    CPU->CarryFlag = (CPU->A > Old_A) ? true : false;
                    // Overflow Flag - Do the sum, but as unsigned, and compare the value of it
                    // with the [unsigned version of the] value we got earlier :)
                    char SignedSum = ((char) Old_A) - (( char) Mem);
                    if (((char) CPU->A) != SignedSum)
                        CPU->OverflowFlag = true;
                    else
                        CPU->OverflowFlag = false;
                
            }
            // CMP/X/Y - Join function for CMP/X/Y.
            void CMPXY(unsigned char Register, unsigned char Mem)
            {
             
                // We'll do this as signed chars... 'cos it's like important.
             
                // Do Register - Mem
                char Result = ((char) Register) - ((char) Mem);
                
                // Set Flags
                    // Negative / Zero flag
                    SetFlags((unsigned char) Result);
                    // Carry Flag - If A >= Mem
                    if (Result >= 0)
                        CPU->CarryFlag = true;
                    else
                        CPU->CarryFlag = false;
                
            }
            
        // Increments / Decrements
            // INC/X/Y - Joint function for INC/X/Y
            void INC(unsigned char* Mem)
            {
             
                // Increase
                *Mem++;
                
                // Set flags
                SetFlags(*Mem);
                
            }
            // DEC/X/Y - Joint function for DEC/X/Y
            void DEC(unsigned char* Mem)
            {
             
                // Decrease
                *Mem--;
                
                // Set flags
                SetFlags(*Mem);
                
            }
            
        // Shifts / Rotates
            // ASL - Arithmetic Shift Left
            void ASL(unsigned char* Mem)
            {
             
                // Set the carry flag to the old bit 7
                CPU->CarryFlag = (bool) ((*Mem & 128) >> 7);
             
                // Shift left
                *Mem = *Mem << 1;
                
                // Set Flags
                SetFlags(*Mem);
                
            }
            // LSR - Logical Shift Right
            void LSR(unsigned char* Mem)
            {
                
                // Set the carry flag to the old bit 7
                CPU->CarryFlag = (bool) (*Mem & 1);
             
                // Shift right
                *Mem = *Mem >> 1;
                
                // Set Flags
                SetFlags(*Mem);
                
            }
            // ROL - Rotate left  
            void ROL(unsigned char* Mem)
            {
                
                // Remember the old carry flag, as we'll need to set bit 0 to it.
                bool Old_CF = CPU->CarryFlag;
                
                // Set the CF to the old bit 7
                CPU->CarryFlag = (bool) ((*Mem & 128) >> 7);
                
                // Shift left
                *Mem = *Mem << 1;
                
                // Set bit 0 to the old carry flag
                *Mem = *Mem & ((int) Old_CF);
                
                // Set Flags
                SetFlags(*Mem);
                
            }
            // ROR - Rotate right 
            void ROR(unsigned char* Mem)
            {
                
                // Remember the old carry flag, as we'll need to set bit 7 to it.
                bool Old_CF = CPU->CarryFlag;
                
                // Set the CF to the old bit 0
                CPU->CarryFlag = (bool) (*Mem & 1);
                
                // Shift right
                *Mem = *Mem >> 1;
                
                // Set bit 7 to the old carry flag
                *Mem = *Mem & (((int) Old_CF) << 7);
                
                // Set Flags
                SetFlags(*Mem);
                
            }

        // Jumps & Calls
            // JMP - Jump to Adr
            void JMP(unsigned short Adr)
            {
             
                // Set PC to the Adr
                CPU->PC = Adr;
                
            }
            // JSR - Save PC to stack and jump to sub!
            void JSR(unsigned short Adr)
            {
             
                  //  Push PC to stack
                  RAM->PushToStack(CPU->PC);
                  
                  // Set PC to Adr
                  CPU->PC = Adr;
                
            }
            // RTS - Pull PC from stack
            void RTS()
            {
             
                // Get PC from stack
                CPU->PC = RAM->PullFromStack();
                
            }
            
        // Branches
            // BCC - Branch if carry flag clear
            void BCC(char Loc)
            {
             
                // Branch..
                if (CPU->CarryFlag)
                    CPU->PC += Loc;
                
            }
            // BCS - Branch if carry flag set
            void BCS(unsigned char Loc)
            {
                
                // Branch..
                if (!CPU->CarryFlag)
                    CPU->PC += Loc;
                    
            }
            // BEQ - Branch if zero flag set
            void BEQ(unsigned char Loc)
            {
             
                // Branch..
                if (CPU->ZeroFlag)
                    CPU->PC += Loc;
                
            }
            // BNE - Branch if zero flag clear    
            void BNE(unsigned char Loc)
            {
             
                // Branch..
                if (!CPU->ZeroFlag)
                    CPU->PC += Loc;
                
            }
            // BMI - Branch if negative flag set   
            void BMI(unsigned char Loc)
            {
             
                // Branch..
                if (CPU->NegativeFlag)
                    CPU->PC += Loc;
                
            }
            // BPL - Branch if negative flag clear
            void BPL(unsigned char Loc)
            {
             
                // Branch..
                if (!CPU->NegativeFlag)
                    CPU->PC += Loc;
                
            }
            // BVS - Branch if overflow flag set
            void BVS(unsigned char Loc)
            {
             
                // Branch..
                if (CPU->OverflowFlag)
                    CPU->PC += Loc;
                
            }
            // BVC - Branch if overflow flag clear
            void BVC(unsigned char Loc)
            {
             
                // Branch..
                if (!CPU->OverflowFlag)
                    CPU->PC += Loc;
                
            }
        
        // Status Flag Changes
            // CLC - Clear carry flag
            void CLC()
            {
             
                // ..
                CPU->CarryFlag = false;
                
            }
            // CLD - Clear decimal flag
            void CLD()
            {
             
                // ..
                CPU->DecimalFlag = false;
                
            }
            // CLI - Clear interrupt disable flag
            void CLI()
            {
             
                // .. 
                CPU->InterruptFlag = false;
                
            }
            // CLV - Clear overflow flag
            void CLV()
            {
             
                // ..
                CPU->OverflowFlag = false;
                
            }
            // SEC - Set carry flag
            void SEC()
            {
             
                // ..
                CPU->CarryFlag = true;
                
            }
            // SED - Set decimal mode flag 
            void SED()
            {
             
                // ..
                CPU->DecimalFlag = true;
                
            }
            // SEI - Set interrupt disable flag 
            void SEI()
            {
             
                // ..
                CPU->InterruptFlag = true;
                
            }
            
        // System Functions
            // BRK - Force an interrupt
            void BRK()
            {
             
                // Push PC and Processor Status to stack
                RAM->PushToStack(CPU->PC + 1);
                RAM->PushToStack(CPU->FlagsToByte());
                
                // Set PC to the interrupt vector at $FFFE/F
                CPU->PC = AbsoluteAddress(RAM->GetMemory(0xFFFE), RAM->GetMemory(0xFFFF));
                
            }
            // RTI - Return from interupt
            void RTI()
            {
                
                // Pull Processor Flags + PC from stack
                CPU->FlagsFromByte(RAM->PullFromStack());
                CPU->PC = RAM->PullFromStack();
                
            }
            // No NOP, as why bother!?
            
        // Other
            // Flags
            void SetFlags(unsigned char Mem)
            {
             
                // Zero Flag
                if (Mem == 0)
                    CPU->ZeroFlag = true;
                else
                    CPU->ZeroFlag = false;
                    
                // Negative Flag
                if (Mem >= 128)
                    CPU->NegativeFlag = true;
                else
                    CPU->NegativeFlag = false;
                
            }  

// </Namespace>
}
